package io.joern.scanners.c

import io.joern.scanners.*
import io.joern.console.*
import io.joern.macros.QueryMacros.*
import io.shiftleft.semanticcpg.language.*

object GJB_8114_R_1_3_6 extends QueryBundle {

  implicit val resolver: ICallResolver = NoResolve

  @q
  def Pointer_Null_After_Free(): Query =
    Query.make(
      name = "Pointer_Null_After_Free",
      author = Crew.Sixpence,
      title = "指针变量被释放后必须置为NULL",
      description = """
                      |当指针变量被释放后，必须将其置为NULL，以避免悬空指针的使用。
                     """.stripMargin,
      score = 2,
      withStrRep({ cpg =>
        cpg.call.name("free").flatMap { freeCall =>
          val freedPointer = freeCall.argument(1).code
          // 查找在free之后是否将指针置为NULL
          val nullAssignment = cpg.method
            .filter(_.fullName == freeCall.method.fullName)
            .assignment
            .filter(_.target.code == freedPointer)
            .filter(_.source.code == "NULL")
            .toList
          if (nullAssignment.isEmpty) {
            Some(freeCall)
          } else {
            None
          }
        }
      }),
      tags = List(QueryTags.default),
      codeExamples = CodeExamples(
        List("""
               |
               |void func() {
               |    int *ptr = malloc(sizeof(int));
               |    free(ptr); // violation
               |}
               |""".stripMargin),
        List("""
               |
               |void func() {
               |    int *ptr = malloc(sizeof(int));
               |    free(ptr);
               |    ptr = NULL; // ok
               |}
               |""".stripMargin)
      )
    )
}
